import configparser
import json
import re
import shutil
import socket
from urllib.request import urlopen

import requests
import yaml

from utils.constants import *
from utils.path import get_file_path
from config.setting import DEBUG

# 全局变量
black_list = []
white_list = []


def _send_requests(url):
    """对目标地址发起请求"""
    resp = urlopen(url).read().decode()
    return resp


def _handle_list(data: str) -> list:
    """将黑名单.txt和白名单.txt的数据，转成list"""
    data_lst = data.split("\n")
    result = []
    for item in data_lst:
        item = item.strip()
        if item.startswith("#") or item == "":
            continue
        result.append(item)
    return result


class Setting:
    """(未使用)读取应用的设置文件"""
    @staticmethod
    def get_setting():
        setting_path = os.path.join(SETTING_FOLDER, "setting.ini")
        if not os.path.exists(setting_path):
            os.makedirs(SETTING_FOLDER)
            default_setting = get_file_path(r"conf_files\default_setting.ini")
            shutil.copy(default_setting, setting_path)

        conf = configparser.ConfigParser()
        conf.read(setting_path, encoding="utf-8")
        return conf


class BlackList:
    @property
    def value(self) -> list:
        global black_list
        if len(black_list) == 0:
            if DEBUG:
                white_path = get_file_path(r"conf_files\blacklist.txt")
                with open(white_path, 'r', encoding="utf-8") as fp:
                    data = fp.read()
            else:
                data = _send_requests(BLACKLIST_URL)
        black_list = _handle_list(data)
        return black_list


class WhiteList:
    @property
    def value(self) -> list:
        global white_list
        if len(white_list) == 0:
            if DEBUG:
                white_path = get_file_path(r"conf_files\whitelist.txt")
                with open(white_path, 'r', encoding="utf-8") as fp:
                    data = fp.read()
            else:
                data = _send_requests(WHITELIST_URL)
            white_list = _handle_list(data)
        return white_list


class Config:
    __instance = None

    def __new__(cls, *args, **kwargs):
        """本机的配置类--单例模式"""
        if not cls.__instance:
            cls.__instance = super().__new__(cls)
            cls.__instance.init_config()
        return cls.__instance

    @classmethod
    def init_config(cls):
        config_data = cls.__instance.load_config()
        cls.target_proxy_server = config_data["target_proxy_server"]
        cls.local_proxy_server = config_data["local_proxy_server"]
        cls.exclude_addresses = config_data["exclude_addresses"]
        cls.proxy_pattern = config_data["proxy_pattern"]
        cls.inform = config_data["inform"]
        cls.config_data = config_data

    def load_config(self) -> dict:
        """获取远端的配置文件，并根据ip筛选出适合本地计算机的配置"""
        if DEBUG:
            config_path = get_file_path(r'conf_files\conf.yml')
            with open(config_path, "r", encoding="utf-8") as fp:
                conf_json = yaml.safe_load(fp)
        else:
            resp = _send_requests(CONFIG_URL)
            conf_json = yaml.safe_load(resp)

        conf_keys = list(conf_json.keys())
        ips = self.get_local_ipv4()
        _, conf_index = self.find_most_similar_elements(ips, conf_keys)
        if conf_index is None:
            raise ValueError("ip错误，请联系管理员")
        config_data = conf_json[conf_keys[conf_index]]
        return config_data

    def to_dict(self):
        return self.config_data

    @staticmethod
    def get_local_ipv4() -> list:
        """获取当前计算机所有的ipv4地址"""
        adds = socket.getaddrinfo(socket.gethostname(), None, family=socket.AF_INET)
        ipv4 = []
        for item in adds:
            network_ip = item[-1][0]
            ipv4.append(network_ip)
        return ipv4

    @staticmethod
    def find_most_similar_elements(lst1: list, lst2: list) -> tuple:
        """
        找到最大相似元素的下标
        lst1 = ['192.168.10.5', '169.254.153.1', '169.254.171.221', '192.168.1.3']
        lst2 = ['192.168.10', '192.168.40', '192.168.220', '192.168.10.5']
        :return: (0, 3)
        """
        result_idx = (None, None)
        for local_index, local_ip in enumerate(lst1):
            three_bit = re.findall(r"\d*\.\d*\.\d*", local_ip)[0]
            four_bit = local_ip
            for remote_index, remote_ip in enumerate(lst2):
                if three_bit == remote_ip:
                    result_idx = (local_index, remote_index)
                elif four_bit == remote_ip:
                    result_idx = (local_index, remote_index)
        return result_idx


class Version:
    """软件的版本控制类，版本号，软件更新都在这里实现。同样采用单例模式"""
    __instance = None

    def __new__(cls, *args, **kwargs):
        if not cls.__instance:
            cls.__instance = super().__new__(cls)
            cls.__instance.init_version()
        return cls.__instance

    @classmethod
    def init_version(cls):
        version_data = cls.__instance.get_local_version()
        cls.app_name = version_data["appName"]
        cls.version_num = version_data["version"]
    
    @staticmethod
    def get_local_version() -> dict:
        """获取本地软件的版本信息"""
        with open(LOCAL_VERSION_PATH, "r", encoding="utf-8") as fp:
            data = json.load(fp)
        return data

    def update_msg(self) -> dict:
        """本地的版本号与远端版本号，进行比较，如果检测有更新，返回更新日志"""
        msg = {}
        if not DEBUG:
            resp = _send_requests(NEW_VERSION_URL)
            resp_dict = json.loads(resp, strict=False)
            remote_version = resp_dict["version"]
            local_version = self.version_num
            if remote_version > local_version:
                msg = {
                    "description": resp_dict["description"],
                    "new_version": remote_version,
                    "update_url": resp_dict["updateUrl"]
                }
        return msg
    def start_update(self, update_url):
        app_name = update_url.split("/")[-1]
        current_path = os.path.dirname(os.getcwd())
        file_path = os.path.join(current_path, app_name)

        app_content = requests.get(update_url)
        pass
